package com.alameda.service.impl.media;

import com.alameda.common.base.constant.MediaConstant;
import com.alameda.common.exception.BusinessException;
import com.alameda.common.utils.CollectionUtils;
import com.alameda.common.utils.ExcelUtils;
import com.alameda.common.utils.ObjectUtils;
import com.alameda.common.utils.PubUtils;
import com.alameda.facade.media.AdMediaFacade;
import com.alameda.mapper.media.AdMediaMapper;
import com.alameda.mapper.tag.AdTagMapper;
import com.alameda.mapper.type.AdMediaTypeMapper;
import com.alameda.media.bo.AdMediaBO;
import com.alameda.media.dto.AdMediaDTO;
import com.alameda.media.vo.AdMediaVO;
import com.alameda.pojo.media.AdMediaDO;
import com.alameda.service.media.AdMediaService;
import com.alameda.service.tag.AdTagMiddleService;
import com.alameda.service.type.AdMediaMiddleService;
import com.alameda.tag.dto.AdTagDTO;
import com.alameda.tag.vo.AdTagMiddleVO;
import com.alameda.type.dto.AdMediaTypeDTO;
import com.alameda.type.vo.AdMediaMiddleVO;
import com.alameda.video.dto.ApVideoDTO;
import com.alameda.video.vo.ApVideoVO;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

import static com.alameda.common.base.constant.MediaConstant.StringConstant.COMMA;
import static com.alameda.common.base.constant.MediaConstant.StringConstant.EMPTY_STRING;

/**
 * 媒体文件 业务逻辑层 具体实现
 *
 * @author Lv
 * @date 2024/07/20
 */
@Slf4j
@Service
public class AdMediaServiceImpl extends ServiceImpl<AdMediaMapper, AdMediaDO> implements AdMediaService {

    @Autowired
    private AdMediaMapper adMediaMapper;

    /**
     * 保存媒体文件
     *
     * @param adMediaDTO 媒体文件信息
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public AdMediaVO saveMedia(AdMediaDTO adMediaDTO) {
        return saveAndUploadMedia(adMediaDTO);
    }

    /**
     * 批量上传文件
     *
     * @param adMediaDTOList 媒体文件信息
     * @return
     */
    @Override
    public Boolean uploadFileBatch(List<AdMediaDTO> adMediaDTOList) {
        try {
            adMediaDTOList.forEach(this::saveAndUploadMedia);
        } catch (Exception e) {
            BusinessException.throwException(e.getMessage());
        }

        return Boolean.TRUE;
    }

    /**
     * 查询视频列表
     *
     * @param apVideoDTO 移动端视频信息
     * @return
     */
    @Override
    public List<ApVideoVO> loadVideo(ApVideoDTO apVideoDTO) {
        List<AdMediaDO> adMediaDOList = adMediaMapper.findByFileName(apVideoDTO.getVideoName(), apVideoDTO.getLoadCount());
        return adMediaDOList.stream().map(this::convertAdMediaDoToApVideoVo).collect(Collectors.toList());
    }

    /**
     * 数据模型转换
     *
     * @param adMediaDO 媒体文件
     * @return
     */
    public ApVideoVO convertAdMediaDoToApVideoVo(AdMediaDO adMediaDO) {
        ApVideoVO apVideoVO = new ApVideoVO();
        apVideoVO.setId(adMediaDO.getId());
        apVideoVO.setVideoName(dealFileName(adMediaDO.getFileName()));
        apVideoVO.setVideoCover(adMediaDO.getFileCover());
        return apVideoVO;
    }

    /**
     * 查询视频
     *
     * @param apVideoDTO 移动端视频信息
     * @return
     */
    @Override
    public ApVideoVO findById(ApVideoDTO apVideoDTO) {
        AdMediaDO adMediaDO = this.getById(apVideoDTO.getId());

        ApVideoVO apVideoVO = new ApVideoVO();
        apVideoVO.setUrl(adMediaDO.getFileUrl());
        apVideoVO.setVideoName(dealFileName(adMediaDO.getFileName()));
        apVideoVO.setVideoCover(adMediaDO.getFileCover());
        apVideoVO.setId(adMediaDO.getId());
        return apVideoVO;
    }

    /**
     * 读取视频文件信息
     *
     * @param idList 视频文件id集合
     * @return
     */
    @Override
    public List<AdMediaVO> findByVideoIdIn(List<Long> idList) {
        List<AdMediaDO> adMediaDOList = this.findByIdInOrderByCreateTimeDesc(idList);
        adMediaDOList.forEach(item -> dealFileName(item.getFileName()));
        return ObjectUtils.convertList(adMediaDOList, AdMediaVO.class);
    }

    /**
     * 处理文件名称
     *
     * @param fileName 文件名称
     * @return
     */
    @Override
    public String dealFileName(String fileName) {
        return PubUtils.isEmpty(fileName) ? EMPTY_STRING : dealNameReplace(fileName);
    }

    /**
     * 分页查询媒体文件
     *
     * @param adMediaDTO 媒体文件信息
     * @return
     */
    @Override
    public IPage<AdMediaVO> loadMedia(AdMediaDTO adMediaDTO) {
        IPage<AdMediaDO> page = getLoadPage(adMediaDTO);

        Set<Long> mediaIdList = getIdCondition(adMediaDTO);
        IPage<AdMediaDO> pageData = adMediaMapper.findPageData(page, adMediaDTO.getFileName(), mediaIdList);
        IPage<AdMediaVO> adMediaPage = ObjectUtils.convertPageData(pageData, AdMediaVO.class);

        dealPageData(adMediaPage);
        return adMediaPage;
    }

    /**
     * 获取分页对象
     *
     * @param adMediaDTO 媒体文件信息
     * @return
     */
    private IPage<AdMediaDO> getLoadPage(AdMediaDTO adMediaDTO) {
        return new Page<>(adMediaDTO.getPageNum(), adMediaDTO.getPageSize());
    }

    @Autowired
    private AdMediaTypeMapper adMediaTypeMapper;

    @Autowired
    private AdTagMapper adTagMapper;

    /**
     * 获取id集合作为查询条件
     *
     * @param adMediaDTO 媒体文件标签信息
     * @return
     */
    private Set<Long> getIdCondition(AdMediaDTO adMediaDTO) {
        Long mediaTypeId = adMediaDTO.getMediaTypeId();
        // 通过分类id读取视频id集合
        List<Long> mediaIdFirstList = PubUtils.isNotNull(mediaTypeId)
                ? adMediaTypeMapper.findByMediaTypeId(mediaTypeId)
                : CollectionUtils.newArrayList();

        Long mediaTagId = adMediaDTO.getMediaTagId();
        // 通过标签id读取视频id集合
        List<Long> mediaIdSecondList = PubUtils.isNotNull(mediaTagId)
                ? adTagMapper.findByMediaTagId(mediaTagId, mediaIdFirstList)
                : CollectionUtils.newArrayList();

        return mergeMediaList(mediaIdFirstList, mediaIdSecondList);
    }

    /**
     * 合并视频文件id集合
     *
     * @param mediaIdFirstList  通过分类读取视频id
     * @param mediaIdSecondList 通过标签取视频id
     * @return
     */
    private Set<Long> mergeMediaList(List<Long> mediaIdFirstList, List<Long> mediaIdSecondList) {
        // 取通过标签读取媒体id集合
        if (PubUtils.isNotEmpty(mediaIdSecondList)) {
            return CollectionUtils.newHashSet(mediaIdSecondList);
        }

        mediaIdFirstList = PubUtils.isEmpty(mediaIdFirstList) ? CollectionUtils.newArrayList() : mediaIdFirstList;
        mediaIdSecondList = PubUtils.isEmpty(mediaIdSecondList) ? CollectionUtils.newArrayList() : mediaIdSecondList;

        mediaIdFirstList.addAll(mediaIdSecondList);
        return CollectionUtils.newHashSet(mediaIdFirstList);
    }

    /**
     * 处理分页数据
     *
     * @param adMediaPage 分页数据
     */
    private void dealPageData(IPage<AdMediaVO> adMediaPage) {
        List<AdMediaVO> adMediaVOList = adMediaPage.getRecords();
        if (PubUtils.isEmpty(adMediaVOList)) {
            return;
        }

        List<Long> mediaIdList = adMediaVOList.stream().map(AdMediaVO::getId).collect(Collectors.toList());
        // 读取分类信息
        List<AdMediaMiddleVO> adMediaMiddleVOList = adMediaMiddleService.findByMediaIdList(mediaIdList);

        // 合并媒体和分类集合
        mergeMediaAndType(adMediaVOList, adMediaMiddleVOList);

        //读取标签信息
        List<AdTagMiddleVO> adTagMiddleVOList = adTagMiddleService.findByMediaInList(mediaIdList);
        // 合并并媒体和标签集合
        mergeMediaAndTag(adMediaVOList, adTagMiddleVOList);
    }

    /**
     * 合并并媒体和标签集合
     *
     * @param adMediaVOList     媒体文件数据显示对象
     * @param adTagMiddleVOList 媒体文件标签关联数据显示对象
     * @return
     */
    private List<AdMediaVO> mergeMediaAndTag(List<AdMediaVO> adMediaVOList, List<AdTagMiddleVO> adTagMiddleVOList) {
        if (PubUtils.isEmpty(adTagMiddleVOList) || PubUtils.isEmpty(adMediaVOList)) {
            return adMediaVOList;
        }

        adMediaVOList.forEach(adMediaVO -> {
            Long mediaId = adMediaVO.getId();
            List<AdTagMiddleVO> adTagMiddleList = getAdTagMiddleListByMediaId(mediaId, adTagMiddleVOList);
            if (PubUtils.isNotEmpty(adTagMiddleList)) {
                adMediaVO.setMediaTagIdList(getAdTagMiddleIdList(adTagMiddleList));
                adMediaVO.setTagNames(getAdTagMiddleNames(adTagMiddleList));
            }
        });
        return adMediaVOList;
    }

    /**
     * 获取标签名称字符串 a,b,c……
     *
     * @param adTagMiddleList 标签关联信息
     * @return
     */
    private String getAdTagMiddleNames(List<AdTagMiddleVO> adTagMiddleList) {
        return adTagMiddleList.stream()
                .sorted(Comparator.comparing(AdTagMiddleVO::getTagCreateTime).reversed())
                .map(AdTagMiddleVO::getTagName)
                .collect(Collectors.joining(COMMA));
    }

    /**
     * 获取标签关联信息id集合
     *
     * @param adTagMiddleList 标签关联信息
     * @return
     */
    private List<Long> getAdTagMiddleIdList(List<AdTagMiddleVO> adTagMiddleList) {
        return adTagMiddleList.stream()
                .sorted(Comparator.comparing(AdTagMiddleVO::getTagCreateTime).reversed())
                .map(AdTagMiddleVO::getTagId)
                .collect(Collectors.toList());
    }

    /**
     * 获取媒体文件对应标签关联信息
     *
     * @param mediaId           媒体文件id
     * @param adTagMiddleVOList 标签关联信息
     * @return
     */
    private List<AdTagMiddleVO> getAdTagMiddleListByMediaId(Long mediaId, List<AdTagMiddleVO> adTagMiddleVOList) {
        return adTagMiddleVOList.stream().filter(item -> mediaId.equals(item.getMediaId())).collect(Collectors.toList());
    }

    /**
     * 合并媒体和分类集合
     *
     * @param adMediaVOList       媒体信息集合
     * @param adMediaMiddleVOList 媒体分类集合
     * @return
     */
    private List<AdMediaVO> mergeMediaAndType(List<AdMediaVO> adMediaVOList, List<AdMediaMiddleVO> adMediaMiddleVOList) {
        if (PubUtils.isEmpty(adMediaMiddleVOList)) {
            return adMediaVOList;
        }
        // 转换为map集合（id,对象）方便获取
        Map<Long, AdMediaMiddleVO> adMediaMiddleMap = adMediaMiddleVOList.stream().collect(Collectors.toMap(AdMediaMiddleVO::getMediaId, v -> v, (k, v) -> v));

        // 封装媒体分类信息
        adMediaVOList.forEach(item -> {
            AdMediaMiddleVO adMediaMiddleVO = adMediaMiddleMap.get(item.getId());
            if (PubUtils.isNotNull(adMediaMiddleVO)) {
                item.setMediaTypeId(adMediaMiddleVO.getMediaTypeId());
                item.setMediaTypeName(adMediaMiddleVO.getTypeName());
            }
        });

        return adMediaVOList;
    }

    /**
     * 上传媒体文件封面
     *
     * @param adMediaDTO 媒体文件信息
     * @return
     */
    @Override
    public String uploadMediaCover(AdMediaDTO adMediaDTO) {
        log.info("子类方法com.alameda.service.impl.media.AdMediaServiceImplExt.uploadMediaCover实现");
        return EMPTY_STRING;
    }

    @Autowired
    private AdTagMiddleService adTagMiddleService;

    @Autowired
    private AdMediaMiddleService adMediaMiddleService;

    /**
     * 修改媒体文件信息
     *
     * @param adMediaDTO 媒体文件信息
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean updateMedia(AdMediaDTO adMediaDTO) {
        Boolean aBoolean = adMediaFacade.updateMediaHistory(adMediaDTO);
        log.info("播放记录修改结果:{}", aBoolean);

        Boolean resultTypeChange = adMediaMiddleService.updateTypeInfo(adMediaDTO);
        log.info("分类修改结果:{}", resultTypeChange);

        Boolean resultTagChange = adTagMiddleService.updateTagInfo(adMediaDTO);
        log.info("标签修改结果:{}", resultTagChange);

        AdMediaDO adMediaDO = ObjectUtils.convert(adMediaDTO, AdMediaDO.class);
        return this.updateById(adMediaDO);
    }

    /**
     * 导出媒体excel
     *
     * @param adMediaDTO 媒体文件信息
     */
    @Override
    public void exportExcel(AdMediaDTO adMediaDTO) {
        List<AdMediaBO> adMediaBOList = getExportData(adMediaDTO);
        ExcelUtils.export(adMediaBOList, AdMediaBO.class, "媒体信息");
    }

    @Autowired
    private AdMediaFacade adMediaFacade;

    /**
     * 删除媒体文件
     *
     * @param adMediaDTO 媒体文件信息
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean removeMedia(AdMediaDTO adMediaDTO) {
        log.info("删除媒体文件:{}", JSONObject.toJSONString(adMediaDTO));
        // 删除播放记录
        adMediaFacade.removeHistoryByMediaIdList(adMediaDTO.getIdList());
        // 删除标签关联关系
        adTagMiddleService.removeTagByMediaIdList(adMediaDTO.getIdList());
        // 删除对应视频弹幕
        adMediaFacade.removeDanmakuByMediaIdList(adMediaDTO.getIdList());
        // 删除文件信息
        return this.removeBatchByIds(adMediaDTO.getIdList());
    }

    /**
     * 获取媒体信息详情
     *
     * @param adMediaDTO 媒体文件信息
     * @return
     */
    @SuppressWarnings("all")
    @Override
    public AdMediaVO findMediaDetail(AdMediaDTO adMediaDTO) {
        AdMediaDO adMediaDO = this.getById(adMediaDTO.getId());
        AdMediaVO adMediaVO = ObjectUtils.convert(adMediaDO, AdMediaVO.class);

        List<AdMediaMiddleVO> adMediaMiddleVOList = adMediaMiddleService.findByMediaIdList(Arrays.asList(adMediaVO.getId()));
        // 合并分类结果
        mergeMediaAndType(Arrays.asList(adMediaVO), adMediaMiddleVOList);

        List<AdTagMiddleVO> adTagMiddleVOList = adTagMiddleService.findByMediaInList(Arrays.asList(adMediaVO.getId()));
        // 合并标签结果
        mergeMediaAndTag(Arrays.asList(adMediaVO), adTagMiddleVOList);
        return adMediaVO;
    }

    /**
     * 查询视频播放页面列表视频
     *
     * @param apVideoDTO 移动端视频信息
     * @return
     */
    @Override
    public List<ApVideoVO> findVideoDetailInfo(ApVideoDTO apVideoDTO) {
        List<AdMediaDO> adMediaDOList = adMediaMapper.findByIdGreaterThanUnionLessThan(apVideoDTO.getId());
        return adMediaDOList.stream().map(this::convertAdMediaDoToApVideoVo).collect(Collectors.toList());
    }

    /**
     * 通过标签查询视频列表
     *
     * @param adTagDTO 媒体文件标签信息
     * @return
     */
    @Override
    public List<ApVideoVO> loadMediaByTag(AdTagDTO adTagDTO) {
        List<AdMediaDO> adMediaDOList = adMediaMapper.findByTagId(adTagDTO.getId(), adTagDTO.getLoadCount());
        List<ApVideoVO> apVideoVOList = adMediaDOList.stream().map(this::convertAdMediaDoToApVideoVo).collect(Collectors.toList());
        apVideoVOList.forEach(this::dealName);
        return apVideoVOList;
    }

    /**
     * 通过类型查询视频列表
     *
     * @param adMediaTypeDTO 媒体文件分类信息
     * @return
     */
    @Override
    public List<ApVideoVO> loadMediaByType(AdMediaTypeDTO adMediaTypeDTO) {
        List<AdMediaDO> adMediaDOList = adMediaMapper.findByTypeId(adMediaTypeDTO.getId(), adMediaTypeDTO.getLoadCount());
        List<ApVideoVO> apVideoVOList = adMediaDOList.stream().map(this::convertAdMediaDoToApVideoVo).collect(Collectors.toList());
        apVideoVOList.forEach(this::dealName);
        return apVideoVOList;
    }

    /**
     * 物理层面移除已逻辑删除文件
     *
     * @return
     */
    @Override
    public Boolean removeLogicDeletedFile() {
        log.info("子类com.alameda.service.impl.media.AdMediaServiceImplExt.removeLogicDeletedFile方法实现");
        return Boolean.TRUE;
    }

    /**
     * 物理层面删除已被替换文件封面
     *
     * @return
     */
    @Override
    public Boolean removeReplacedCover() {
        log.info("子类com.alameda.service.impl.media.AdMediaServiceImplExt.removeReplacedCover方法实现");
        return Boolean.TRUE;
    }

    /**
     * 处理视频文件名称
     *
     * @param apVideoVO 频文件
     */
    private void dealName(ApVideoVO apVideoVO) {
        String videoName = apVideoVO.getVideoName();
        byte[] bytes = videoName.getBytes(StandardCharsets.UTF_8);
        String dealResult = bytes.length > MediaConstant.LoadMediaConstant.DEAL_INDEX ? getExtractedName(videoName) : videoName;
        apVideoVO.setVideoName(dealResult);
    }

    /**
     * 获取截取后的视频文件名称
     *
     * @param videoName 视频文件名称
     * @return
     */
    private String getExtractedName(String videoName) {
        String extractedString = videoName.substring(0, Math.min(videoName.length(), 25));
        return extractedString + MediaConstant.LoadMediaConstant.REPLACE_STR;
    }

    /**
     * 获取导出数据
     *
     * @param adMediaDTO 媒体文件信息
     * @return
     */
    private List<AdMediaBO> getExportData(AdMediaDTO adMediaDTO) {
        List<Long> idList = adMediaDTO.getIdList();
        IPage<AdMediaVO> adMediaPage = null;
        if (PubUtils.isEmpty(idList)) {
            adMediaPage = loadMedia(adMediaDTO);
        } else {
            IPage<AdMediaDO> page = getLoadPage(adMediaDTO);
            IPage<AdMediaDO> pageData = adMediaMapper.findPageData(page, null, idList);
            adMediaPage = ObjectUtils.convertPageData(pageData, AdMediaVO.class);
            dealPageData(adMediaPage);
        }
        return ObjectUtils.convertList(adMediaPage.getRecords(), AdMediaBO.class);
    }

    /**
     * 处理文件名称替换
     *
     * @param fileName 文件名称
     * @return
     */
    private String dealNameReplace(String fileName) {
        return fileName.replace(MediaConstant.FileFormat.MP_4, EMPTY_STRING)
                .replace(MediaConstant.FileFormat.MOV, EMPTY_STRING)
                .replace(MediaConstant.FileFormat.AVI, EMPTY_STRING);
    }

    /**
     * 上传文件
     *
     * @param adMediaDTO 媒体文件信息
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public AdMediaVO saveAndUploadMedia(AdMediaDTO adMediaDTO) {
        log.info("子类方法 com.alameda.service.impl.media.AdMediaServiceImplExt.saveAndUploadMedia 实现");
        return AdMediaVO.builder().build();
    }
}
